﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using MathematicallySafe.Base;
using MathematicallySafe.HeadCounter.Data.Common;
using MathematicallySafe.HeadCounter.Data.Interfaces;
using MathematicallySafe.HeadCounter.Data.Models;
using StructureMap;

namespace MathematicallySafe.HeadCounter.Data.Parsers
{
    // todo add interface and loose coupling for this.
    public class ItemXmlParser
    {
        private LootItem _item;

        public LootItem MakeLootItem(XElement xml)
        {
            _item = new LootItem();

            try
            {
                _item.ID = Convert.ToInt32(xml.Element("id").Value);
                _item.Name = xml.Element("name").Value;
                _item.Icon = xml.Element("icon").Value;
                _item.iLevel = Convert.ToInt32(xml.Element("itemLevel").Value);

                ParseUnique(xml);
                ParseStamina(xml);
                ParseIntellect(xml);
                ParseSpirit(xml);
                ParseAgility(xml);
                ParseStrength(xml);

                ParseAttackPower(xml);
                ParseSpellPower(xml);
                ParseHasteRating(xml);
                ParseCriticalStrikeRating(xml);
                ParseDefenseRating(xml);
                ParseDodgeRating(xml);
                ParseHitRating(xml);
                ParseArmourPenetrationRating(xml);
                ParseSpellData(xml.Element("spellData"));

                ParseEquipData(xml.Element("equipData"));
                ParseDamageData(xml.Element("damageData"));
                ParseSocketData(xml.Element("socketData"));

                ParseItemSource(xml.Element("itemSource"));

                CheckForOverride();
            }
            catch (Exception ex)
            {
                ObjectFactory.GetInstance<ILog<ItemXmlParser>>().Error("Exception parsing item " + _item.ID + "(" + ex.Message + ")");
                _item.Name = ex.Message;
            }

            return _item;
        }

        private void ParseItemSource(XElement xml)
        {
            if (xml == null) return;

            var dropType = xml.Attribute("value").Value;

            if (dropType == "sourceType.creatureDrop")
            {
                _item.Zone = xml.Attribute("areaName").Value;
                _item.Boss = xml.Attribute("creatureName").Value;
            }
            else if (dropType == "sourceType.gameObjectDrop")
            {
                _item.Zone = "";
                _item.Boss = "Chest Drop";
            }
            else if (dropType == "sourceType.questReward")
            {
                _item.Zone = "";
                _item.Boss = "Quest Reward";
            }
        }

        private void ParseSocketData(XElement socketData)
        {
            if (socketData == null) return;

            _item.Sockets = new List<SocketType>();

            var sockets = socketData.Elements("socket");
            sockets.ForEach(s =>
                                {
                                    var color = s.Attribute("color").Value;
                                    _item.Sockets.Add((SocketType)Enum.Parse(typeof(SocketType), color));
                                });
        }

        private void ParseDamageData(XElement damageData)
        {
            var damage = damageData.Element("damage");
            if (damage == null) return;

            var speed = damageData.Element("speed");
            var dps = damageData.Element("dps");

            _item.Damage = new DamageData()
                               {
                                   Min = Convert.ToInt32(damage.Element("min").Value),
                                   Max = Convert.ToInt32(damage.Element("max").Value),
                                   Speed = speed != null ? Convert.ToDouble(speed.Value) : 0,
                                   DPS = dps != null ? Convert.ToDouble(dps.Value) : 0.0
                               };
        }

        private void ParseSpellData(XElement spellData)
        {
            if (spellData == null) return;

            CheckCreateBonuses();

            var spells = spellData.Elements("spell");
            spells.ForEach(s =>
                               {
                                   var trigger = Convert.ToInt32((string) s.Element("trigger").Value);
                                   var desc = s.Element("desc").Value;

                                   var bonus = "";
                                   switch (trigger)
                                   {
                                       case 0:
                                           bonus = "Use: ";
                                           break;
                                       case 1:
                                           bonus = "Use: ";
                                           break;
                                       case 2:
                                           bonus = "Chance on hit: ";
                                           break;
                                   }

                                   bonus += desc;
                                   _item.Bonuses.Add(bonus);
                               });
        }


        private LootSlot ParseSlot(int slot)
        {
            // todo stick this in an array or something.
            switch (slot)
            {
                case 1:
                    return LootSlot.Head;
                case 2:
                    return LootSlot.Neck;
                case 3:
                    return LootSlot.Shoulder;
//                case 4:
//                    return LootSlot.Legs;
                case 5:
                    return LootSlot.Chest;
                case 6:
                    return LootSlot.Waist;
                case 7:
                    return LootSlot.Legs;
                case 8:
                    return LootSlot.Feet;
                case 9:
                    return LootSlot.Wrist;
                case 10:
                    return LootSlot.Hands;
                case 11:
                    return LootSlot.Finger;
                case 12:
                    return LootSlot.Trinket;
                case 13:
                    return LootSlot.OneHand;
                case 14:
                    return LootSlot.OffHand;
                case 15:
                    // Bows and Guns
                    return LootSlot.Ranged;
                case 16:
                    return LootSlot.Back;
                case 17:
                    return LootSlot.TwoHand;
//                case 18:
//                    return LootSlot.Legs;
//                case 19:
//                    return LootSlot.Legs;
                case 20:
                    // todo why do some chest items come back as 20 and not 5?
                    return LootSlot.Chest;
                case 21:
                    return LootSlot.MainHand;
                case 23:
                    return LootSlot.OffHand;
                case 24:
                    // This is actually Hunter arrows or bullets
                    return LootSlot.Other;
                case 25:
                    return LootSlot.Thrown;
                case 26:
                    return LootSlot.Ranged;
                case 28:
                    return LootSlot.Relic;
                default:
                    return LootSlot.Other;
            }
        }

        private ItemSubClass ParseSubClass(string subclassName)
        {
            subclassName = subclassName.Replace(" ", "");
            return (ItemSubClass)Enum.Parse(typeof(ItemSubClass), subclassName);
        }

        private void ParseEquipData(XElement xml)
        {
            _item.Slot = ParseSlot(Convert.ToInt32(xml.Element("inventoryType").Value));

            var subClass = xml.Element("subclassName");
            if (subClass != null)
                _item.SubClass = ParseSubClass(subClass.Value);
            else
                _item.SubClass = ItemSubClass.None;
        }

        private void ParseUnique(XElement xml)
        {
            var count = xml.Element("maxCount");
            if (count != null)
            {
                _item.Unique = Convert.ToInt32(count.Value) == 1;
            }
            else
            {
                _item.Unique = false;
            }
        }

        private void ParseIntellect(XElement xml)
        {
            var bonus = xml.Element("bonusIntellect");
            if (bonus != null)
            {
                _item.Intellect = Convert.ToInt32(bonus.Value);
            }
        }

        private void ParseStrength(XElement xml)
        {
            var bonus = xml.Element("bonusStrength");
            if (bonus != null)
            {
                _item.Strength = Convert.ToInt32(bonus.Value);
            }
        }

        private void ParseAgility(XElement xml)
        {
            var bonus = xml.Element("bonusAgility");
            if (bonus != null)
            {
                _item.Agility = Convert.ToInt32(bonus.Value);
            }
        }

        private void ParseSpirit(XElement xml)
        {
            var bonus = xml.Element("bonusSpirit");
            if (bonus != null)
            {
                _item.Spirit = Convert.ToInt32(bonus.Value);
            }
        }

        private void ParseStamina(XElement xml)
        {
            var bonus = xml.Element("bonusStamina");
            if (bonus != null)
            {
                _item.Stamina = Convert.ToInt32(bonus.Value);
            }
        }

        private void ParseAttackPower(XElement xml)
        {
            var bonus = xml.Element("bonusAttackPower");
            if (bonus != null)
            {
                CheckCreateBonuses();
                _item.Bonuses.Add(string.Format("Equip: Increases attack power by {0}", bonus.Value));
            }
        }

        private void ParseHasteRating(XElement xml)
        {
            var bonus = xml.Element("bonusHasteRating");
            if (bonus != null)
            {
                CheckCreateBonuses();
                _item.Bonuses.Add(string.Format("Equip: Increases haste rating by {0}", bonus.Value));
            }
        }

        private void ParseSpellPower(XElement xml)
        {
            var bonus = xml.Element("bonusSpellPower");
            if (bonus != null)
            {
                CheckCreateBonuses();
                _item.Bonuses.Add(string.Format("Equip: Increases spell power by {0}", bonus.Value));
            }
        }

        private void ParseArmourPenetrationRating(XElement xml)
        {
            var bonus = xml.Element("bonusArmorPenetration");
            if (bonus != null)
            {
                CheckCreateBonuses();
                _item.Bonuses.Add(string.Format("Equip: Increases your armor penetration by {0}", bonus.Value));
            }
        }

        private void ParseCriticalStrikeRating(XElement xml)
        {
            var bonus = xml.Element("bonusCritRating");
            if (bonus != null)
            {
                CheckCreateBonuses();
                _item.Bonuses.Add(string.Format("Equip: Improves critical strike rating by {0}", bonus.Value));
            }
        }

        private void ParseHitRating(XElement xml)
        {
            var bonus = xml.Element("bonusHitRating");
            if (bonus != null)
            {
                CheckCreateBonuses();
                _item.Bonuses.Add(string.Format("Equip: Improves hit rating by {0}", bonus.Value));
            }
        }

        private void ParseDefenseRating(XElement xml)
        {
            var bonus = xml.Element("bonusDefenseSkillRating");
            if (bonus != null)
            {
                CheckCreateBonuses();
                _item.Bonuses.Add(string.Format("Equip: Improves defense rating by {0}", bonus.Value));
            }
        }

        private void ParseDodgeRating(XElement xml)
        {
            var bonus = xml.Element("bonusDodgeRating");
            if (bonus != null)
            {
                CheckCreateBonuses();
                _item.Bonuses.Add(string.Format("Equip: Improves dodge rating by {0}", bonus.Value));
            }
        }

        private void CheckCreateBonuses()
        {
            if (_item.Bonuses == null) _item.Bonuses = new List<string>();
        }

        private void CheckForOverride()
        {
            var lootOverride = ObjectFactory.GetInstance<IOptions>().Overrides.FirstOrDefault(o => o.id == _item.ID);

            if (lootOverride != null)
            {
                _item.Slot = lootOverride.Slot;
                _item.iLevel = lootOverride.iLevel;
            }
        }
    }
}